#import "DYFCryptoUtils+PPPExt.h"
@implementation DYFCryptoUtils (PPPExt)
+ (NSData *)stripPublicKeyHeader:(NSData *)d_key {
	if (d_key == nil) return(nil);
	unsigned long len = [d_key length];
	if (!len) return(nil);
	unsigned char *c_key = (unsigned char *)[d_key bytes];
	unsigned int  idx    = 0;
	if (c_key[idx++] != 0x30) return(nil);
	if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
	else idx++;
	static unsigned char seqiod[] = { 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
		0x01, 0x05, 0x00 };
	if (memcmp(&c_key[idx], seqiod, 15)) return(nil);
	idx += 15;
	if (c_key[idx++] != 0x03) return(nil);
	if (c_key[idx] > 0x80) idx += c_key[idx] - 0x80 + 1;
	else idx++;
	if (c_key[idx++] != '\0') return(nil);
	return([NSData dataWithBytes:&c_key[idx] length:len - idx]);
}
+ (NSData *)RSAEncryptData:(NSData *)plainData publicKeyRef:(SecKeyRef)pubKeyRef {
	size_t cipherBufferSize = SecKeyGetBlockSize(pubKeyRef);
	uint8_t *cipherBuffer   = malloc(cipherBufferSize * sizeof(uint8_t));
	size_t blockSize        = cipherBufferSize - 11;
	size_t blockCount       = (size_t)ceil([plainData length] / (double)blockSize);
	NSMutableData *encryptedData = [NSMutableData data];
	for (int i = 0; i < blockCount; i++) {
		unsigned long bufferSize = MIN(blockSize, [plainData length] - i * blockSize);
		NSData *buffer  = [plainData subdataWithRange:NSMakeRange(i * blockSize, bufferSize)];
		OSStatus status = SecKeyEncrypt(pubKeyRef,
										kSecPaddingPKCS1,
										(const uint8_t *)[buffer bytes],
										[buffer length],
										cipherBuffer,
										&cipherBufferSize);
		if (status != noErr) {
			return nil;
		}
		NSData *encryptedBytes = [[NSData alloc] initWithBytes:(const void *)cipherBuffer length:cipherBufferSize];
		[encryptedData appendData:encryptedBytes];
	}
	if (cipherBuffer){
		free(cipherBuffer);
	}
	return encryptedData;
}
+ (SecKeyRef)getPublicKeyRef:(NSString *)key {
	NSRange spos = [key rangeOfString:@"-----BEGIN PUBLIC KEY-----"];
	NSRange epos = [key rangeOfString:@"-----END PUBLIC KEY-----"];
	if (spos.location != NSNotFound && epos.location != NSNotFound) {
		NSUInteger s = spos.location + spos.length;
		NSUInteger e = epos.location;
		NSRange range = NSMakeRange(s, e-s);
		key = [key substringWithRange:range];
	}
	key = [key stringByReplacingOccurrencesOfString:@"\r" withString:@""];
	key = [key stringByReplacingOccurrencesOfString:@"\n" withString:@""];
	key = [key stringByReplacingOccurrencesOfString:@"\t" withString:@""];
	key = [key stringByReplacingOccurrencesOfString:@" "  withString:@""];
	NSData *data = [self base64DecodedDataWithString:key];
	data = [self stripPublicKeyHeader:data];
	if (!data) {
		return nil;
	}
	NSString *tag = @"cr_rsa_pubkey";
	NSData *d_tag = [NSData dataWithBytes:[tag UTF8String] length:[tag length]];
	NSMutableDictionary *pubKeyDict = [[NSMutableDictionary alloc] init];
	[pubKeyDict setObject:(__bridge id)kSecClassKey forKey:(__bridge id)kSecClass];
	[pubKeyDict setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
	[pubKeyDict setObject:d_tag forKey:(__bridge id)kSecAttrApplicationTag];
	SecItemDelete((__bridge CFDictionaryRef)pubKeyDict);
	[pubKeyDict setObject:data forKey:(__bridge id)kSecValueData];
	[pubKeyDict setObject:(__bridge id)kSecAttrKeyClassPublic forKey:(__bridge id)
	 kSecAttrKeyClass];
	[pubKeyDict setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)
	 kSecReturnPersistentRef];
	CFTypeRef persistKey = nil;
	OSStatus status = SecItemAdd((__bridge CFDictionaryRef)pubKeyDict, &persistKey);
	if (persistKey != nil) {
		CFRelease(persistKey);
	}
	if ((status != noErr) && (status != errSecDuplicateItem)) {
		return nil;
	}
	[pubKeyDict removeObjectForKey:(__bridge id)kSecValueData];
	[pubKeyDict removeObjectForKey:(__bridge id)kSecReturnPersistentRef];
	[pubKeyDict setObject:[NSNumber numberWithBool:YES] forKey:(__bridge id)kSecReturnRef];
	[pubKeyDict setObject:(__bridge id)kSecAttrKeyTypeRSA forKey:(__bridge id)kSecAttrKeyType];
	SecKeyRef keyRef = nil;
	status = SecItemCopyMatching((__bridge CFDictionaryRef)pubKeyDict, (CFTypeRef *)&keyRef);
	if(status != noErr){
		return nil;
	}
	return keyRef;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
+ (NSString *)RSAEncrypt:(NSString *)string publicKey:(NSString *)pubKey {
	NSData *plainData     = [string dataUsingEncoding:NSUTF8StringEncoding];
	SecKeyRef pubKeyRef   = [self getPublicKeyRef:pubKey];
	if (pubKeyRef == NULL) {
		return @"";
	}
	NSData *encryptedData = [self RSAEncryptData:plainData publicKeyRef:pubKeyRef];
	CFRelease(pubKeyRef);
	return [self base64EncodedStringWithData:encryptedData];
}
@end
